#include "StdAfx.h"
#include "DatabaseManager.h"

CDatabaseManager::CDatabaseManager(void)
    : m_hDatabaseFile(NULL)
    , m_RecordSize(0)
{
}

CDatabaseManager::~CDatabaseManager(void)
{
}

DWORD
CDatabaseManager::Open(
    const TCHAR *const DatabaseFileName,
    UINT RecordSize,
    BOOL ClearIfExists)
{
    if (RecordSize == 0)
        return ERROR_INVALID_PARAMETER;

    DWORD CreationDisposition = (ClearIfExists ? CREATE_ALWAYS : OPEN_ALWAYS);

    m_hDatabaseFile = CreateFile(
        DatabaseFileName,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        CreationDisposition,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (m_hDatabaseFile == INVALID_HANDLE_VALUE)
    {
        DWORD Err = GetLastError();
        if (Err == ERROR_SUCCESS)
            Err = ERROR_OPEN_FAILED;

        return Err;
    }

    m_RecordSize = RecordSize;

    return ERROR_SUCCESS;
}

void CDatabaseManager::Close()
{
    CloseHandle(m_hDatabaseFile);
    m_hDatabaseFile = NULL;
}

DWORD
CDatabaseManager::ReadRecord(
    UINT RecordIndex,
    BYTE *RecordData,
    UINT RecordDataLength,
    UINT *pRecordDataActualLength)
{
    if (m_hDatabaseFile == NULL)
        return ERROR_INVALID_STATE;

    if (RecordData == NULL || RecordDataLength == 0)
        return ERROR_INVALID_PARAMETER;

    if (RecordDataLength < m_RecordSize)
        return ERROR_INSUFFICIENT_BUFFER;

    DWORD FileSize = GetFileSize(m_hDatabaseFile, NULL);
    LONG FilePointer = RecordIndex * m_RecordSize;
    if ((DWORD)FilePointer > FileSize - m_RecordSize)
        return ERROR_INVALID_INDEX;

    DWORD Ret = SetFilePointer(
        m_hDatabaseFile,
        FilePointer,
        NULL,
        FILE_BEGIN);
    if (Ret == 0xFFFFFFFF)
        return ERROR_SEEK;

    DWORD BytesRead;
    BOOL Read = ReadFile(
        m_hDatabaseFile,
        RecordData,
        m_RecordSize,
        &BytesRead,
        NULL);
    if (Read == FALSE)
        return ERROR_READ_FAULT;

    if (BytesRead != m_RecordSize)
        return ERROR_GEN_FAILURE;

    if (pRecordDataActualLength)
        *pRecordDataActualLength = BytesRead;

    return ERROR_SUCCESS;
}

DWORD
CDatabaseManager::WriteRecord(
    UINT RecordIndex,
    BYTE *RecordData,
    UINT RecordDataLength)
{
    if (m_hDatabaseFile == NULL)
        return ERROR_INVALID_STATE;

    if (RecordData == NULL || RecordDataLength == 0)
        return ERROR_INVALID_PARAMETER;

    if (RecordDataLength < m_RecordSize)
        return ERROR_INSUFFICIENT_BUFFER;

    LONG FilePointer = RecordIndex * m_RecordSize;
    DWORD Ret = SetFilePointer(
        m_hDatabaseFile,
        FilePointer,
        NULL,
        FILE_BEGIN);
    if (Ret == 0xFFFFFFFF)
        return ERROR_SEEK;

    DWORD BytesWritten = 0;
    BOOL Write = WriteFile(
        m_hDatabaseFile,
        RecordData,
        m_RecordSize,
        &BytesWritten,
        NULL);
    if (Write == FALSE)
        return ERROR_WRITE_FAULT;

    if (BytesWritten != m_RecordSize)
        return ERROR_GEN_FAILURE;

    return ERROR_SUCCESS;
}

DWORD CDatabaseManager::GetRecordCount(UINT *pRecordCount)
{
    if (m_hDatabaseFile == NULL)
        return ERROR_INVALID_STATE;

    if (pRecordCount == NULL)
        return ERROR_INVALID_PARAMETER;

    DWORD FileSize = GetFileSize(m_hDatabaseFile, NULL);
    if (FileSize == 0xFFFFFFFF)
    {
        ATLASSERT(GetLastError() != ERROR_SUCCESS);
        return GetLastError();
    }

    *pRecordCount = FileSize / m_RecordSize;
    return ERROR_SUCCESS;
}

DWORD CDatabaseManager::DeleteRecord(UINT RecordIndex)
{
    UINT RecordCount = 0;
    DWORD Err = GetRecordCount(&RecordCount);
    if (Err != ERROR_SUCCESS)
        return Err;

    if (RecordCount == 0)
        return ERROR_SUCCESS;   // Nothing to delete

    BYTE *pRecord = new BYTE[m_RecordSize];
    if (pRecord == NULL)
        return ERROR_OUTOFMEMORY;

    Err = ReadRecord(RecordIndex + 1, pRecord, m_RecordSize, NULL);
    while (Err == ERROR_SUCCESS)
    {
        (void)WriteRecord(RecordIndex, pRecord, m_RecordSize);

        RecordIndex++;
        Err = ReadRecord(RecordIndex + 1, pRecord, m_RecordSize, NULL);
    }

    delete[] pRecord;

    LONG FilePointer = (RecordCount - 1) * m_RecordSize;
    Err = SetFilePointer(
        m_hDatabaseFile,
        FilePointer,
        NULL,
        FILE_BEGIN);
    if (Err == 0xFFFFFFFF)
        return ERROR_SEEK;

    BOOL Ret = SetEndOfFile(m_hDatabaseFile);
    if (Ret == FALSE)
        return ERROR_GEN_FAILURE;

    return ERROR_SUCCESS;
}
